/******************************************************************************
 * Copyright 2022 The Airos Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "pc5/module_udp.h"
#include <iostream>
#include <string>
#include <utility>
#include <glog/logging.h>

namespace air {
namespace net {
ModuleUdp::ModuleUdp() {}

ModuleUdp::~ModuleUdp() {
  close(sock_);
}

bool ModuleUdp::Init(const std::string& peer_ip, int peer_port,
                     int local_port) {
  sock_ = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock_ < 0) {
    LOG(WARNING) << "socket init false";
    return false;
  }
  LOG(INFO) << "socket init true";

  // set send
  memset(&addr_out_, 0, sizeof(struct sockaddr_in));
  addr_out_.sin_family = AF_INET;
  addr_out_.sin_port = htons(peer_port);
  addr_out_.sin_addr.s_addr = inet_addr(peer_ip.data());
  LOG(INFO) << "peer ip port:" << peer_ip << " " << peer_port;

  // set recv
  memset(&addr_in_, 0, sizeof(struct sockaddr_in));
  addr_in_.sin_family = AF_INET;
  addr_in_.sin_port = htons(local_port);
  addr_in_.sin_addr.s_addr = htonl(INADDR_ANY);
  auto ret = bind(sock_, (struct sockaddr*)&addr_in_, sizeof(addr_in_));
  if (ret < 0) {
    LOG(WARNING) << "socket bind false:" << local_port;
    return false;
  }
  LOG(WARNING) << "bind local port true:" << local_port;

  return true;
}

bool ModuleUdp::Recv(std::string* str_out) {
  if (!str_out) {
    return false;
  }

  struct sockaddr_in addr_recv;
  int len = sizeof(addr_recv);
  char data[8192];
  int recv_len =
      recvfrom(sock_, data, sizeof(data), 0, (struct sockaddr*)&addr_recv,
               reinterpret_cast<socklen_t*>(&len));
  if (recv_len <= 0) {
    return false;
  }
  std::string str(data, recv_len);

  if (!HeaderDecode(str, str_out)) {
    LOG(WARNING) << "parse header error!";
    return false;
  }

  return true;
}

bool ModuleUdp::Send(const std::string& data, int aid) {
  if (data.empty()) {
    return false;
  }

  std::string str_out;
  HeaderEncode(data, &str_out, aid);

  sendto(sock_, str_out.data(), str_out.size(), 0, (struct sockaddr*)&addr_out_,
         sizeof(addr_out_));

  return true;
}

bool ModuleUdp::HeaderEncode(const std::string& str_in, std::string* str_out,
                             int aid) {
  if (str_in.empty() || !str_out) {
    return false;
  }

  str_out->assign(str_in);

  return true;
}
bool ModuleUdp::HeaderDecode(const std::string& str_in, std::string* str_out) {
  if (str_in.empty() || !str_out) {
    return false;
  }

  str_out->assign(str_in);

  return true;
}

int ModuleUdp::GetCbr() { return cbr_; }

}  // namespace net
}  // namespace air
